本文介绍了TensorFlow的基本数据类型和使用方法。

本文中tf都指代的是TensorFlow

Introduction

  • TensorFlow中可以认为只有两种基本数据结构:tensor 和 operation。operation代表一个操作,对应计算图中的节点,也可以认为是函数;tensor本质是一个句柄,代表的是operation的计算结果,也可认为是函数的返回值

tf.Tensor

  • tensor的中文意思是“张量”,可将其理解为高维向量,向量是一维tensor,矩阵是二维tensor。
  • Tensor底层的值是由numpy.array来实现的,因此两者间会有很多共通之处。
  • 关于tensor和op的关系:A Tensor is a symbolic handle to one of the outputs of an Operation。即tensor本质是一个句柄,绑定着op的结果,因此一个tensor必然会有一个operation(但一个op可以输出多个tensor),所以tensor中才会有Tensor.op属性,表示是什么op产生了这个tensor。
  • 也正因为tensor是句柄,并不存储值,只存储计算结果,所以只有经过计算,tensor才会有相应的值,因此只有运行Session后才能得出tensor的值。

tensor命名规则

  • tensor的命名格式为<op_name>:<output_index>
  • 对返回tensor的API,会自动创建op,并通过name参数指定的操作的名字(即op_name);如果后续有相同命名的操作,则在op_name后加_1;对于其产生的tensor,则按产生顺序自动在后面加:0/:1/:2等。
  • 一般op_name命名为大写开头。
  • example code:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    W1 = tf.Variable([[1,1,1],[2,2,2]],dtype = tf.float32,name='Var') 
    W2 = tf.Variable([[1,1,1],[2,2,2]],dtype = tf.float32,name='Var')
    A1 = tf.add(W1, W2)
    B1, B2 = tf.split(W1, 2)
    C1 = tf.constant(value=[1,2,3], name="Con1")
    P1 = tf.placeholder(tf.int32, shape=(2,3), name="Pla1")
    print(W1)
    print(W2)
    print(A1)
    print(B1)
    print(B2)
    print(C1)
    print(P1)

    # <tf.Variable 'Var:0' shape=(2, 3) dtype=float32_ref>
    # <tf.Variable 'Var_1:0' shape=(2, 3) dtype=float32_ref>
    # Tensor("Add:0", shape=(2, 3), dtype=float32)
    # Tensor("split:0", shape=(1, 3), dtype=float32)
    # Tensor("split:1", shape=(1, 3), dtype=float32)
    # Tensor("Con1:0", shape=(3,), dtype=int32)
    # Tensor("Pla1:0", shape=(2, 3), dtype=int32

Rank of Tensors

rank指tensor的秩,即维数。不同rank的tensor会有不同的数学意义:

rank 数学意义
0 Scalar(标量)
1 Vector(向量)
2 Matrix(矩阵)
3 Cube(立方体)
4 一行Cube(立方体再向高维抽象)
  • 如何获得tensor的rank:因为其不是Tensor的属性之一,所以无法直接访问对象获得,只能通过tf.rank(some_Tensor)方法来获取维数(返回的一个封装了rank值的tensor)
  • 访问tensor中的某个scalar:每一维指定对应的标号即可。
    eg. my_scalar = my_matrix[1, 2], my_column_vector = my_matrix[:, 3]。其中:表示取这一维度中的所有数。

Shape of Tensors

  • shape表示tenor各维度的长度,或者说各维度中元素的个数。
    eg. tf.constant([[[1., 2., 3.]], [[7., 8., 9.]]]):rank为3,shape为[2, 1, 3]。
  • tensor的shape可以只指定一部分,如TensorShape([None, 256]),只指定了列数为256列,行数会在运行时自动被推断出。
  • 如何获得tensor的shape:
    1. 通过tf.Tensor.shape属性(推荐):返回一个TensorShape对象,通过slice机制来访问封装在TensorShape中各个维度的长度;
    2. 通过tf.shape(my_tensor)方法:返回一个Tensor对象,表示原tensor的shape。因为是tensor类型,所以必须运行后才能知道其具体值。但好处因为是在运行时计算的,所以此时shape一定是fully-defined的。
  • 如何改变tensor的shape:通过tf.reshape()方法。
    1
    2
    3
    4
    5
    6
    7
    8
    rank_three_tensor = tf.ones([3, 4, 5])

    matrix = tf.reshape(rank_three_tensor, [6, 10])
    # Reshape existing content into a 6x10 matrix

    matrixB = tf.reshape(matrix, [3, -1])
    # Reshape existing content into a 3x20 matrix.
    # -1 tells reshape to calculate the size of this dimension.

Data Types of Tensors

  • 每个tf.tensor都有一个dtype属性,表示tensor中存储的数据的数据类型,通过tf.tensor.dtype访问。
  • 常用数据类型有:tf.int32(对应python中的整型), tf.float32(对应python中的浮点型)。
  • 如何转换tensor类型:float_tensor = tf.cast(tf.constant([1, 2, 3]), dtype=tf.float32)

如何得到tensor的值

  • 通过tf.Tensor.eval()方法,它其实是tf.get_default_session().run(tf.Tensor)方法的简写。
  • 注1:eval()方法必须在默认的session被激活的情况下才可执行(通过with tf.Session().as_default():语句块)。
  • 注2:eval方法同样可以传入feed_dict,如t.eval(feed_dict={p:2.0})

tf.Variable

  • tf.Variable为tensorflow中的变量,值可以通过附加在其上的操作更改。
  • 声明一个Variable后,系统会生成四个相关操作:v_name/initial_value, v_name, v_name/Assign, v_name/read, tf即通过这四个op实现对Variable值的更改。

tensor和variable的关系

  • variable对应tf.Variable类型,tensor应该对应的是tf.Tensor。从内部实现上讲,variable本质是对tensor的一种封装。
  • TensorFlow官方将tf.Variable定义为Tensor-like objects,即类tensor类型(其他还有numpy.ndarray, list以及其他python基本类型: bool, float, int, str),这些类型在传入函数时会被隐式转换为tensor类型(using tf.convert_to_tensor() method),因此也可以当作tensor来使用。
  • tf.Variable实现的意义在于提供一种值可以更改的类tensor类型。因为tensor本身实质上属于常量,值一旦初始化是不可更改的(tf.constant()tf.placeholder()都只是产生tensor的方法,返回值是tensor)。
  • 所以如果要在graph中训练参数,那么参数只能是variable形式(tensor不能改变值),tensor则用来表示输入、输出值,在graph中传递中间的计算值。

定义Variables

  • 官方推荐使用tf.get_variable()函数来定义、初始化以及调用变量,配合tf.variable_scope来确保不会不小心重用或声明新的变量。
  • 就不要用tf.Variable()。
  • tf.get_variable()方法原型:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    tf.get_variable(
    name,
    shape=None,
    dtype=None,
    initializer=None,
    regularizer=None,
    trainable=None,
    collections=None,
    caching_device=None,
    partitioner=None,
    validate_shape=True,
    use_resource=None,
    custom_getter=None,
    constraint=None,
    synchronization=tf.VariableSynchronization.AUTO,
    aggregation=tf.VariableAggregation.NONE
    )

其主要有两个作用:

  1. 计算图中不存在name参数的值时,会声明一个变量,等价于调用variable的构造函数tf.Variable()
  2. 计算图中已存在name参数时,返回对应的变量,因此可通过这个方法来根据名字获取变量;
  • 详细参数说明:
    name:必填参数,用于指定变量的名字;
    shape:指定变量的shape;
    initializer:指定变量的初始器,默认为tf.glorot_uniform_initializer
    collections:指定变量属于的集合,默认为GraphKeys.GLOBAL_VARIABLES

初始化variable

variable在使用前必须初始化,初始化变量有两步:

  1. 在声明变量时指定initializer参数,其可以是一个
    tf.initializer对象,也可以是一个tensor。若是initializer则会按该initializer的方式来初始化,若是tensor则会用该tensor的值来初始化。默认初始器参数为tf.glorot_uniform_initializer
  2. 调用tf.global_variables_initializer()方法,对tf.GraphKeys.GLOBAL_VARIABLES集合中的变量进行初始化(
    按变量指定的initializer参数进行初始化)。

使用variable

  • 因为variable是tensor-like类型,所以直接像普通tensor一样使用variable即可。

    1
    2
    v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
    w = v + 1
  • 如何为variable重新分配值:使用tf.Variable.assign()tf.Variable.assign_add()tf.Variable.assign_sub()三个方法,可用对Variable重新赋值。

    1
    2
    3
    4
    v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
    assignment = v.assign_add(1) # assign方法会返回一个新分配值的tensor
    tf.global_variables_initializer().run()
    sess.run(assignment)

Variable collections

tf提供了collection机制,使得变量的存储和访问变得很方便。系统常用collections:

  • tf.GraphKeys.GLOBAL_VARIABLES:全局共享的变量(每个设备都可以访问),变量默认都存储在这个集合中。
  • tf.GraphKeys.TRAINABLE_VARIABLES:存储会被训练的变量,tf.Optimizer就是优化此集合下的变量。
    注:所有变量默认都是存储在TRAINABLE_VARIABLESGLOBAL_VARIABLES这两个集合中。TRAINABLE_VARIABLES属于GLOBAL_VARIABLES的一个子集,两者并不冲突。
  • tf.GraphKeys.LOCAL_VARIABLES:用于存储临时变量,在这个集合中的变量不会被优化。
  • 如何将变量添加到指定集合中:
    1. 在声明变量的时候指定collections参数
    2. 通过调用tf.add_to_collection(collection_name, variable_name)函数。

tf.variable_scope机制

tf.variable_scope的作用有两点:

  1. 对变量使用范围加以限制,以方便变量的create和share,在一个scope中构造的变量就只能在这个scope中使用。
  2. 起到tf.name_scope的作用,对变量命名进行整理。
  • reuse = True:总是重用变量(变量没有的话会出错),reuse = tf.AUTO_REUSE:如果变量没有会新建一个变量,有的话则重用。
  • example code:

    1
    2
    3
    4
    5
    6
    7
    def my_image_filter(input_images):
    with tf.variable_scope("layer1"):
    tf.get_variable(name='Var', shape=[1])
    # "layer1/Var"
    with tf.variable_scope("layer2"):
    tf.get_variable(name='Var', shape=[1])
    # 会重新定义一个"layer2/Var"变量,而非重用之前的变量
  • 如果确实希望重用相同的内部变量,可通过如下方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # example 1
    with tf.variable_scope("layer1"):
    V1 = tf.get_variable(name='Var', shape=[1])
    with tf.variable_scope("layer1"):
    V2 = tf.get_variable(name='Var') # 报错,禁止重用变量

    # example 2
    with tf.variable_scope("layer1"):
    V1 = tf.get_variable(name='Var', shape=[1])
    V2 = tf.get_variable(name='Var') # 报错,禁止重用变量

    # example 3
    with tf.variable_scope("layer1"):
    V1 = tf.get_variable(name='Var', shape=[1])
    # "layer1/Var"
    with tf.variable_scope("layer1", reuse=True):
    V2 = tf.get_variable(name='Var')
    # 正确, V1, V2重用同一个变量

tf.variable_scope和tf.name_scope的区别

  • name_scope:仅仅是为了更好地管理变量的命名空间而提出的,对变量的使用没有任何影响。此外tensorboard会将一个name_scope下的变量整理在一个父节点中,因此良好的name scope命名规则会很有利于模型可视化。
  • variable_scope:不仅仅是管理命名空间,还是为了限制变量的使用范围,常和 tf.get_variable()配合使用,来实现变量共享的功能。tensorboard也会对同一variable_scope下的变量进行整理。
  • 可以认为variable_scope是name_scope针对variable的“加强版”。

tf.constant

tf.constant是常量tensor,其在声明的时候就定义好值了,且值不可改变。
eg. tf.constant(3.0, dtype=tf.float32)

tf.placeholder

占位符,声明的时候只需要指定类型即可,在运算的时候再指定值。

1
2
3
4
5
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
z = x + y
print(sess.run(z, feed_dict={x: 3, y: 4.5}))
print(sess.run(z, feed_dict={x: [1, 3], y: [2, 4]}))

  • 注:placeholder不能eval(),其值只能通过feed_dict的形式传入。
  • feed_dict:将值以字典的形式传入placeholder,字典的键可以为:
    • placeholder的变量名,直接传入变量名;
    • placeholder的name属性,字符串类型,但必须满足<op_name>:<output_index>的格式(tf中tensor的命名规范),否则系统无法将其对应到相应的placeholder上。

Post Date: 2019-02-21

版权声明: 本文为原创文章,转载请注明出处